package ongoing

import (
	"database/sql"
	"errors"
	"fmt"
	"sync"

	_ "github.com/lib/pq"
	"time"
)


type Engine struct {
	db    *sql.DB
	mutex sync.Mutex
}

func NewEngine() *Engine {
	db, err := sql.Open("postgres", fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", db_user, db_password, db_name))
	if err != nil {
		panic(err)
	}
	engine := &Engine{
		db:    db,
		mutex: sync.Mutex{},
	}
	return engine

}

func (e *Engine) Close() {
	e.db.Close()
}

func (e *Engine) AddUser(u *User) error {
	e.mutex.Lock()
	defer e.mutex.Unlock()
	stmt, err := e.db.Prepare("insert into t_user (name,passwd,stars) values ($1,$2,$3) returning id;")
	if err != nil {
		return err
	}
	defer stmt.Close()

	u.Stars = 0
	_, err = stmt.Exec(u.Name, u.Passwd, u.Stars)
	if err != nil {
		return err
	}
	return nil
}

func (e *Engine) GetUserById(id int) (*User, error) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	user := new(User)

	err := e.db.QueryRow("select * from t_user where id=$1", id).Scan(&user.ID, &user.Name, &user.Passwd, &user.Stars)
	if err != nil {
		return nil, err
	}
	user.Passwd = ""

	return user, err
}

func (e *Engine) GetUserByName(name string) (*User, error) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	user := new(User)

	err := e.db.QueryRow("select * from t_user where name=$1", name).Scan(&user.ID, &user.Name, &user.Passwd, &user.Stars)
	if err != nil {
		return nil, err
	}

	return user, err
}

func (e *Engine) GetAllUsers() ([]User, error) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	users := make([]User, 0)

	rows, err := e.db.Query("select * from t_user")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		user := User{}
		rows.Scan(&user.ID, &user.Name, &user.Passwd, &user.Stars)
		users = append(users, user)
	}

	return users, nil
}

func (e *Engine) UpdateUser(u *User) error {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	stmt, err := e.db.Prepare("update t_user set name=$1,passwd=$2,stars=$3 where id=$4")
	if err != nil {
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(u.Name, u.Passwd, u.Stars, u.ID)
	if err != nil {
		return err
	}
	return nil
}

func (e *Engine) CheckUser(username, password string) bool {

	user, err := e.GetUserByName(username)
	if err != nil {
		return false
	}

	if user.Passwd == password {
		return true
	} else {
		return false
	}

}

func (e *Engine) IsUserExist(username string) bool {

	user, err := e.GetUserByName(username)
	if err != nil {
		return false
	}

	if user.Name == "" {
		return false
	} else {
		return true
	}
}

func (e *Engine) GetMissionById(id int) (*Mission, error) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	m := new(Mission)

	err := e.db.QueryRow("select * from t_mission where id=$1", id).Scan(
		&m.ID,
		&m.UserName,
		&m.Mission,
		&m.Description,
		&m.TimeLimit,
		&m.DelayTimes,
		&m.DelayReason,
		&m.Star,
		&m.Status,
		&m.CreateTime,
		&m.SelfComment,
	)
	if err != nil {
		return nil, err
	}
	return m, nil
}

func (e *Engine) GetMissionsByUserName(username string) ([]Mission, error) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	ms := make([]Mission, 0)

	rows, err := e.db.Query("select * from t_mission where username=$1", username)
	if err != nil {
		return nil, err
	}

	for rows.Next() {
		m := Mission{}
		rows.Scan(
			&m.ID,
			&m.UserName,
			&m.Mission,
			&m.Description,
			&m.TimeLimit,
			&m.DelayTimes,
			&m.DelayReason,
			&m.Star,
			&m.Status,
			&m.CreateTime,
			&m.SelfComment,
		)
		ms = append(ms, m)
	}

	return ms, nil
}

func (e *Engine) GetALLMissions() ([]Mission, error) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	ms := make([]Mission, 0)

	rows, err := e.db.Query("select * from t_mission order by createtime")
	if err != nil {
		return nil, err
	}

	for rows.Next() {
		m := Mission{}
		rows.Scan(
			&m.ID,
			&m.UserName,
			&m.Mission,
			&m.Description,
			&m.TimeLimit,
			&m.DelayTimes,
			&m.DelayReason,
			&m.Star,
			&m.Status,
			&m.CreateTime,
			&m.SelfComment,
		)
		ms = append(ms, m)
	}

	return ms, nil

}

func (e *Engine) AddMission(m *Mission) error {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	var count int
	e.db.QueryRow("select count(*) from t_mission where status='进行中' and username=$1", m.UserName).Scan(&count)

	if count >= 3 {
		return errors.New("每人只能创建三个任务")
	}

	stmt1, err := e.db.Prepare("insert into t_mission (username,mission,description,timelimit,delaytimes,delayreason,star,status,createtime,selfcomment) values ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) returning id;")
	if err != nil {
		return err
	}
	defer stmt1.Close()

	m.DelayTimes = 0
	m.DelayReason = ""
	m.Star = 5
	m.Status = "进行中"
	m.CreateTime = time.Now().Format("2006-01-02")
	m.SelfComment = ""

	_, err = stmt1.Exec(
		&m.UserName,
		&m.Mission,
		&m.Description,
		&m.TimeLimit,
		&m.DelayTimes,
		&m.DelayReason,
		&m.Star,
		&m.Status,
		&m.CreateTime,
		&m.SelfComment,
	)
	if err != nil {
		return err
	}
	return nil
}

func (e *Engine) DelMission(id int) error {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	stmt, err := e.db.Prepare("delete from t_mission where id = $1")
	if err != nil {
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(id)
	if err != nil {
		return err
	}
	return nil
}

func (e *Engine) UpdateMission(m *Mission) error {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	stmt, err := e.db.Prepare("update t_mission set username=$1,mission=$2,description=$3,timelimit=$4,delaytimes=$5,delayreason=$6,star=$7,status=$8,createtime=$9,selfcomment=$10 where id=$11")
	if err != nil {
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(
		&m.UserName,
		&m.Mission,
		&m.Description,
		&m.TimeLimit,
		&m.DelayTimes,
		&m.DelayReason,
		&m.Star,
		&m.Status,
		&m.CreateTime,
		&m.SelfComment,
		&m.ID,
	)
	if err != nil {
		return err
	}
	return nil
}

func (e *Engine) FinishMission(m *Mission) error {

	mission, err := e.GetMissionById(m.ID)
	if err != nil {
		return err
	}
	if mission.Status == "已完成" {
		return errors.New("任务已经完成")
	}
	mission.Status = "已完成"
	mission.SelfComment = m.SelfComment
	err = e.UpdateMission(mission)
	if err != nil {
		return err
	}

	u, err := e.GetUserByName(mission.UserName)
	if err != nil {
		return err
	}

	u.Stars += mission.Star
	err = e.UpdateUser(u)
	if err != nil {
		return err
	}
	return nil
}

func (e *Engine) DelayMission(m *Mission) error {

	mission, err := e.GetMissionById(m.ID)
	if err != nil {
		return err
	}

	if mission.Status == "已完成" {
		return errors.New("任务已经完成")
	}

	mission.TimeLimit = m.TimeLimit
	mission.DelayReason = m.DelayReason
	if mission.DelayTimes >= 3 {
		return errors.New("任务延期次数太多")
	}
	mission.DelayTimes++
	mission.Star--
	err = e.UpdateMission(mission)
	if err != nil {
		return err
	}

	return nil
}

func (e *Engine) UpdateMissionDescription(m *Mission) error {
	mission, err := e.GetMissionById(m.ID)
	if err != nil {
		return err
	}

	if mission.Status == "已完成" {
		return errors.New("任务已经完成")
	}

	mission.Description = m.Description
	err = e.UpdateMission(mission)
	if err != nil {
		return err
	}

	return nil
}
